package com.exam.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.exam.annotation.Cache;
import com.exam.entity.Answer;
import com.exam.entity.Question;
import com.exam.mapper.AnswerMapper;
import com.exam.mapper.QuestionBankMapper;
import com.exam.mapper.QuestionMapper;
import com.exam.service.QuestionService;
import com.exam.utils.RedisUtil;
import com.exam.vo.PageResponse;
import com.exam.vo.QuestionVo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.exam.utils.CommonUtils.setEqualsQueryWrapper;
import static com.exam.utils.CommonUtils.setLikeWrapper;

/**
 * @author 小聋包
 * @date 2024-04-20 14:58
 * @Version 1.0
 */
@Service
@RequiredArgsConstructor
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper,Question> implements QuestionService {

    private final QuestionMapper questionMapper;

    private final AnswerMapper answerMapper;

    private final RedisUtil redisUtil;

    private final QuestionBankMapper questionBankMapper;

    /**
     * 根据题目类型、题库名称、题目内容分页查询题目信息
     *
     * @param questionType 题目类型
     * @param questionBank 题库名称
     * @param questionContent 题目内容
     * @param pageNo 页码
     * @param pageSize 每页条数
     * @return 返回分页后的题目信息
     */
    @Override
    public PageResponse<Question> getQuestion(String questionType, String questionBank, String questionContent, Integer pageNo, Integer pageSize) {
        // 创建一个分页对象，设置页码和每页条数
        IPage<Question> questionIPage=new Page<>(pageNo,pageSize);

        // 创建一个查询条件包装器
        QueryWrapper<Question> wrapper=new QueryWrapper<>();
        // 创建一个存储模糊查询参数的Map
        Map<String,Object> likeQueryParams=new HashMap<>(2);

        // 添加题库名称的模糊查询参数
        likeQueryParams.put("qu_bank_name",questionBank);
        // 添加题目内容的模糊查询参数
        likeQueryParams.put("qu_content",questionContent);
        // 设置模糊查询条件
        setLikeWrapper(wrapper,likeQueryParams);
        // 设置精确查询条件，题目类型等于questionType
        setEqualsQueryWrapper(wrapper, Collections.singletonMap("qu_type",questionType));

        // 调用questionMapper的selectPage方法，根据分页对象和查询条件查询题目信息
        questionIPage=questionMapper.selectPage(questionIPage,wrapper);

        // 创建一个PageResponse对象，设置数据和总数，然后返回
        return PageResponse.<Question>builder()
                .data(questionIPage.getRecords())
                .total(questionIPage.getTotal())
                .build();
    }

    /**
     * 根据题目ID列表删除题目和对应的答案信息，并清除Redis缓存
     *
     * @param questionIds 题目ID列表，多个ID以逗号分隔
     */
    @Override
    public void deleteQuestionByIds(String questionIds) {
        // 将题目ID列表按照逗号分割成字符串数组
        String[] ids = questionIds.split(",");
        // 创建一个HashMap对象，用于存储查询条件
        Map<String,Object> map = new HashMap<>();
        // 遍历题目ID数组
        for (String id:ids){
            // 清空HashMap对象
            map.clear();
            // 将题目ID作为查询条件放入HashMap对象中
            map.put("question_id",id);
            // 1. 删除数据库的题目信息
            // 调用questionMapper的deleteById方法，传入题目ID的整数类型参数
            questionMapper.deleteById(Integer.parseInt(id));
            // 2. 删除答案表对应当前题目id的答案
            // 调用answerMapper的deleteByMap方法，传入包含题目ID的HashMap对象作为查询条件

            answerMapper.deleteByMap(map);
            // 3. 移除redis缓存
            // 调用redisUtil的del方法，传入以"questionVo:"为前缀和题目ID拼接的字符串作为缓存的key
            redisUtil.del("questionVo:"+id);
        }
        // 调用redisUtil的del方法，传入"questionBanks"作为缓存的key
        //清除题库的缓存
        redisUtil.del("questionBanks");
    }

    /**
     * 添加问题
     *
     * @param questionVo 问题视图对象
     * @Transactional 用于保证方法内的事务性
     */
    @Transactional
    @Override
    public void addQuestion(QuestionVo questionVo) {
        // 创建一个新的问题对象
        Question question = new Question();

        // 设置问题的基础字段
        // 设置基础字段
        question.setQuType(questionVo.getQuestionType());
        setQuestionField(question, questionVo);

        // 设置题目的插图
        // 设置题目插图
        if (questionVo.getImages().length != 0) {
            String QuImages = Arrays.toString(questionVo.getImages());
            question.setImage(QuImages.substring(1, QuImages.length() - 1).replaceAll(" ", ""));
        }

        // 构建题目所属题库名称
        buildBankName(questionVo, question);

        // 将问题对象插入到数据库中
        questionMapper.insert(question);

        // 获取新插入问题的ID
        Integer currentQuId = question.getId();

        // 创建一个StringBuilder对象，用于拼接多选题答案
        // 设置答案对象
        StringBuilder multipleChoice = new StringBuilder();

        // 如果问题类型不是简答题
        if (questionVo.getQuestionType() != 4) {// 不为简答题

            // 创建一个新的答案对象
            Answer answer = new Answer();

            // 设置答案对象的问题ID
            answer.setQuestionId(currentQuId);

            // 创建StringBuilder对象，用于拼接答案的图片信息
            StringBuilder imgs = new StringBuilder();

            // 创建StringBuilder对象，用于拼接答案的内容
            StringBuilder answers = new StringBuilder();

            // 遍历问题视图对象的答案数组
            for (int i = 0; i < questionVo.getAnswer().length; i++) {
                // 如果该答案有图片信息
                if (questionVo.getAnswer()[i].getImages().length > 0) {// 如果该选项有一张图片信息
                    // 将图片信息添加到图片信息StringBuilder对象中
                    imgs.append(questionVo.getAnswer()[i].getImages()[0]).append(",");
                }

                // 构建答案信息，并添加到答案内容StringBuilder对象中
                buildAnswer(answers, questionVo, i, multipleChoice, answer);
            }

            // 构建多选题答案信息，并更新答案对象
            buildMultiQuestionAnswer(questionVo, multipleChoice, answer, imgs, answers);

            // 将答案对象插入到数据库中
            answerMapper.insert(answer);
        }

        // 清除题库缓存
        clearQuestionBankCache(questionVo, redisUtil);
    }


    /**
     * 设置问题对象的属性
     *
     * @param question 问题对象
     * @param questionVo 问题视图对象
     */
    private void setQuestionField(Question question, QuestionVo questionVo) {
        // 设置问题对象的创建时间
        question.setCreateTime(new Date());
        // 设置问题对象的难度级别
        question.setLevel(questionVo.getQuestionLevel());
        // 设置问题对象的解析
        question.setAnalysis(questionVo.getAnalysis());
        // 设置问题对象的内容
        question.setQuContent(questionVo.getQuestionContent());
        // 设置问题对象的创建人
        question.setCreatePerson(questionVo.getCreatePerson());

        // 设置所属题库
        // 将问题视图对象中的题库ID列表转换为字符串
        String bankIds=Arrays.toString(questionVo.getBankId());
        // 去除字符串的首尾逗号和空格，然后设置到问题对象的所属题库ID属性上
        question.setQuBankId(bankIds.substring(1,bankIds.length()-1).replaceAll(" ",""));
    }

    /**
     * 构建题库名称
     *
     * @param questionVo 问题视图对象
     * @param question   问题对象
     */
    private void buildBankName(QuestionVo questionVo, Question question) {
        // 创建一个StringBuilder对象，用于拼接题库名称
        StringBuilder bankNames=new StringBuilder();
        // 遍历问题视图对象中的题库ID列表
        for (Integer integer:questionVo.getBankId()){
            // 调用questionBankMapper的selectById方法，根据题库ID查询题库信息，并获取题库名称
            // 将查询到的题库名称追加到bankNames中，并在名称后添加逗号
            bankNames.append(questionBankMapper.selectById(integer).getBankName()).append(",");
        }
        // 将bankNames转换为字符串
        String names=bankNames.toString();
        // 去掉最后一个逗号
        names=names.substring(0,names.length()-1);
        // 设置问题对象的题库名称属性
        question.setQuBankName(names);
    }

    /**
     * 构建答案
     *
     * @param answers            答案的字符串构建器
     * @param questionVo         问题视图对象
     * @param i                  答案的下标
     * @param multipleChoice     多选题的选项下标字符串构建器
     * @param answer             答案对象
     */
    private void buildAnswer(StringBuilder answers, QuestionVo questionVo, int i, StringBuilder multipleChoice, Answer answer) {
        // 将当前答案添加到答案的字符串构建器中
        answers.append(questionVo.getAnswer()[i].getAnswer()).append(",");

        // 如果问题是多选题
        //设置对的选项的下标值
        if (questionVo.getQuestionType() == 2) {//多选
            // 如果当前选项是正确的
            if (questionVo.getAnswer()[i].getIsTrue().equals("true"))
                // 将当前选项的下标添加到多选题的选项下标字符串构建器中
                multipleChoice.append(i).append(",");

        } else {
            // 如果问题是单选题或判断题
            //单选和判断 都是仅有一个答案
            if (questionVo.getAnswer()[i].getIsTrue().equals("true")) {
                // 设置答案对象中的正确答案下标
                answer.setTrueOption(i + "");
                // 设置答案对象中的答案解析
                answer.setAnalysis(questionVo.getAnswer()[i].getAnalysis());
            }
        }
    }
    /**
     * 构建多选题答案
     *
     * @param questionVo 问题视图对象
     * @param multipleChoice 多选题选项的StringBuilder对象
     * @param answer 答案对象
     * @param imgs 图片地址的StringBuilder对象
     * @param answers 选项的StringBuilder对象
     */
    private void buildMultiQuestionAnswer(QuestionVo questionVo, StringBuilder multipleChoice, Answer answer, StringBuilder imgs, StringBuilder answers) {
        // 如果问题类型为多选题
        if (questionVo.getQuestionType()==2)
            // 设置正确答案选项
            answer.setTrueOption(multipleChoice.substring(0,multipleChoice.toString().length()-1));

        // 将图片地址的StringBuilder对象转换为字符串
        String handleImgs=imgs.toString();
        // 将选项的StringBuilder对象转换为字符串
        String handleAnswers=answers.toString();

        // 如果图片地址不为空
        if (handleImgs.length()!=0)
            // 去掉最后一个逗号
            handleImgs=handleImgs.substring(0,handleImgs.length()-1);

        // 如果选项不为空
        if (handleAnswers.length()!=0)
            // 去掉最后一个逗号
            handleAnswers=handleAnswers.substring(0,handleAnswers.length()-1);

        // 设置答案的图片
        answer.setImages(handleImgs);
        // 设置所有的选项
        answer.setAllOption(handleAnswers);

    }

    /**
     * 清除问题题库缓存
     *
     * @param questionVo 问题视图对象
     * @param redisUtil Redis工具类
     */
    private void clearQuestionBankCache(QuestionVo questionVo, RedisUtil redisUtil) {
        // 遍历问题视图对象中的题库ID列表
        for (Integer bankId:questionVo.getBankId()){
            // 删除Redis中对应的题库问题缓存
            redisUtil.del("questionBankQuestion:"+bankId);
        }
    }


    /**
     * 更新问题
     *
     * @param questionVo 问题视图对象
     */
    @Override
    public void updateQuestion(QuestionVo questionVo) {
        Question question = new Question();

        // 设置基础字段
        // 设置问题类型
        question.setQuType(questionVo.getQuestionType());
        // 设置问题ID
        question.setId(questionVo.getQuestionId());
        // 设置问题其他字段
        setQuestionField(question, questionVo);

        // 设置题目插图
        if (questionVo.getImages() != null && questionVo.getImages().length != 0) {
            String QuImages = Arrays.toString(questionVo.getImages());
            // 去除方括号和空格，得到图片地址列表
            question.setImage(QuImages.substring(1, QuImages.length() - 1).replaceAll(" ", ""));
        }

        // 构建题库名称
        buildBankName(questionVo, question);

        // 更新问题
        questionMapper.update(question, new UpdateWrapper<Question>().eq("id", questionVo.getQuestionId()));

        // 设置答案对象
        StringBuilder multipleChoice = new StringBuilder();
        if (questionVo.getQuestionType() != 4) {// 不为简答题
            Answer answer = new Answer();
            // 设置答案的问题ID
            answer.setQuestionId(questionVo.getQuestionId());
            StringBuilder imgs = new StringBuilder();
            StringBuilder answers = new StringBuilder();
            for (int i = 0; i < questionVo.getAnswer().length; i++) {
                if (questionVo.getAnswer()[i].getImages() != null && questionVo.getAnswer()[i].getImages().length > 0) {// 如果该选项有一张图片信息
                    // 将图片地址追加到imgs字符串中，并用逗号分隔
                    imgs.append(questionVo.getAnswer()[i].getImages()[0]).append(",");
                }
                // 构建答案
                buildAnswer(answers, questionVo, i, multipleChoice, answer);
            }
            // 构建多选题答案
            buildMultiQuestionAnswer(questionVo, multipleChoice, answer, imgs, answers);
            // 更新答案
            answerMapper.update(answer, new UpdateWrapper<Answer>().eq("question_id", questionVo.getQuestionId()));

            // 删除缓存中的问题视图对象
            redisUtil.del("questionVo:" + questionVo.getQuestionId());

            // 清除问题题库缓存
            clearQuestionBankCache(questionVo, redisUtil);
        }
    }

    /**
     * 根据问题ID获取问题视图对象
     *
     * @param id 问题ID
     * @return 问题视图对象
     * @Cache(prefix = "questionVo", suffix = "#id", ttl = 5, timeUnit = TimeUnit.HOURS)
     * 使用缓存注解，设置缓存前缀为"questionVo"，后缀为问题ID，缓存有效期为5小时
     */
    @Cache(prefix = "questionVo", suffix = "#id", ttl = 5, timeUnit = TimeUnit.HOURS)
    @Override
    public QuestionVo getQuestionVoById(Integer id) {
        // 根据问题ID查询问题对象
        Question question = questionMapper.selectById(id);

        // 根据问题ID查询答案对象
        Answer answer = answerMapper.selectOne(new QueryWrapper<Answer>().eq("question_id", id));

        // 根据问题对象和答案对象构建问题视图对象
        return buildQuestionVoByQuestionAndAnswer(question, answer);
    }

    /**
     * 根据问题和答案构建问题视图对象
     *
     * @param question 问题对象
     * @param answer   答案对象
     * @return 构建好的问题视图对象
     */
    public QuestionVo buildQuestionVoByQuestionAndAnswer(Question question, Answer answer) {
        QuestionVo questionVo = new QuestionVo();

        // 设置问题视图对象的字段
        // 设置字段
        questionVo.setQuestionContent(question.getQuContent());
        questionVo.setAnalysis(question.getAnalysis());
        questionVo.setQuestionType(question.getQuType());
        questionVo.setQuestionLevel(question.getLevel());
        questionVo.setQuestionId(question.getId());

        // 如果问题有图片，则将图片字符串拆分为数组并设置到问题视图对象中
        if (question.getImage() != null && !Objects.equals(question.getImage(), "")) {
            questionVo.setImages(question.getImage().split(","));
        }

        questionVo.setCreatePerson(question.getCreatePerson());

        // 设置问题视图对象所属题库
        if (!Objects.equals(question.getQuBankId(), "")) {
            String[] bids = question.getQuBankId().split(",");
            Integer[] bankIds = new Integer[bids.length];
            for (int i = 0; i < bids.length; i++) {
                bankIds[i] = Integer.parseInt(bids[i]);
            }
            questionVo.setBankId(bankIds);
        }

        // 如果答案不为空，则构建答案对象并设置到问题视图对象中
        if (answer != null) {
            String[] allOption = answer.getAllOption().split(",");
            String[] imgs = answer.getImages().split(",");
            QuestionVo.Answer[] qa = new QuestionVo.Answer[allOption.length];

            // 根据问题类型构建答案对象
            if (question.getQuType() != 2) {
                for (int i = 0; i < allOption.length; i++) {
                    QuestionVo.Answer answer1 = new QuestionVo.Answer();
                    answer1.setId(i);
                    answer1.setAnswer(allOption[i]);
                    // 如果答案中有图片，则设置图片到答案对象中
                    if (i <= imgs.length - 1 && !Objects.equals(imgs[i], ""))
                        answer1.setImages(new String[]{imgs[i]});
                    // 如果当前选项为正确答案，则设置isTrue字段和解析字段
                    if (i == Integer.parseInt(answer.getTrueOption())) {
                        answer1.setIsTrue("true");
                        answer1.setAnalysis(answer.getAnalysis());
                    }
                    qa[i] = answer1;
                }
            } else {// 如果是多选题
                // 将正确选项字符串拆分为整数数组
                List<Integer> multiTrueOptions = Arrays.stream(answer.getTrueOption().split(","))
                        .map(Integer::parseInt)
                        .collect(Collectors.toList());
                for (int i = 0; i < allOption.length; i++) {
                    QuestionVo.Answer answer1 = new QuestionVo.Answer();
                    answer1.setId(i);
                    answer1.setAnswer(allOption[i]);
                    // 如果是多选题，则将所有图片设置到答案对象中
                    answer1.setImages(imgs);
                    // 如果当前选项为正确答案，则设置isTrue字段和解析字段
                    if (multiTrueOptions.contains(i)) {
                        answer1.setIsTrue("true");
                        answer1.setAnalysis(answer.getAnalysis());
                    }
                    qa[i] = answer1;
                }
            }

            // 将答案对象数组设置到问题视图对象中
            questionVo.setAnswer(qa);
        }

        return questionVo;
    }

    /**
     * 根据问题ID列表获取问题视图对象列表
     *
     * @param ids 问题ID列表
     * @return 分页响应对象，包含问题视图对象列表和总记录数
     */
    @Override
    public PageResponse<QuestionVo> getQuestionVoByIds(List<Integer> ids) {
        // 根据问题ID列表批量查询问题对象
        List<Question> questions = questionMapper.selectBatchIds(ids);

        // 根据问题ID列表查询答案对象
        List<Answer> answers = answerMapper.selectList(new QueryWrapper<Answer>().in("question_id", ids));

        // 将问题对象转换为问题视图对象列表
        List<QuestionVo> questionVos = questions.stream()
                .map(question -> {
                    // 根据问题ID查询对应的答案对象
                    Answer currentQuestionAnswer = answers.stream()
                            .filter(answer -> answer.getQuestionId().equals(question.getId()))
                            .findFirst()
                            .orElse(null);

                    // 构建问题视图对象
                    return buildQuestionVoByQuestionAndAnswer(question, currentQuestionAnswer);
                // 收集转换后的问题视图对象列表
                }).collect(Collectors.toList());

        // 构建分页响应对象并返回
        return PageResponse.<QuestionVo>builder()
                .data(questionVos)
                .total(questionVos.size())
                .build();
    }

}
